using System;

namespace Kugar.Core
{
  /// <summary>
  /// Represents a globally unique identifier (GUID) with a
  /// shorter string value. Sguid
  /// </summary>
  ///<seealso>
  ///	http://www.singular.co.nz/blog/archive/2007/12/20/shortguid-a-shorter-and-url-friendly-guid-in-c-sharp.aspx
  ///</seealso>
  public struct ShortGuid
  {
    #region Static
 
    /// <summary>
    /// A read-only instance of the ShortGuid class whose value
    /// is guaranteed to be all zeroes.
    /// </summary>
    public static readonly ShortGuid Empty = new ShortGuid(Guid.Empty);
 
    #endregion
 
    #region Fields
 
    Guid _guid;
    string _value;
 
    #endregion
 
    #region Contructors
 
    /// <summary>
    /// Creates a ShortGuid from a base64 encoded string
    /// </summary>
    /// <param name="value">The encoded guid as a
    /// base64 string</param>
    public ShortGuid(string value)
    {
      _value = value;
      _guid = Decode(value);
    }
 
    /// <summary>
    /// Creates a ShortGuid from a Guid
    /// </summary>
    /// <param name="guid">The Guid to encode</param>
    public ShortGuid(Guid guid)
    {
      _value = Encode(guid);
      _guid = guid;
    }
 
    #endregion
 
    #region Properties
 
    /// <summary>
    /// Gets/sets the underlying Guid
    /// </summary>
    public Guid Guid
    {
      get { return _guid; }
      set
      {
        if (value != _guid)
        {
          _guid = value;
          _value = Encode(value);
        }
      }
    }
 
    /// <summary>
    /// Gets/sets the underlying base64 encoded string
    /// </summary>
    public string Value
    {
      get { return _value; }
      set
      {
        if (value != _value)
        {
          _value = value;
          _guid = Decode(value);
        }
      }
    }
 
    #endregion
 
    #region ToString
 
    /// <summary>
    /// Returns the base64 encoded guid as a string
    /// </summary>
    /// <returns></returns>
    public override string ToString()
    {
      return _value;
    }
 
    #endregion
 
    #region Equals
 
    /// <summary>
    /// Returns a value indicating whether this instance and a
    /// specified Object represent the same type and value.
    /// </summary>
    /// <param name="obj">The object to compare</param>
    /// <returns></returns>
    public override bool Equals(object obj)
    {
      if (obj is ShortGuid)
        return _guid.Equals(((ShortGuid)obj)._guid);
      if (obj is Guid)
        return _guid.Equals((Guid)obj);
      if (obj is string)
        return _guid.Equals(((ShortGuid)obj)._guid);
      return false;
    }
 
    #endregion
 
    #region GetHashCode
 
    /// <summary>
    /// Returns the HashCode for underlying Guid.
    /// </summary>
    /// <returns></returns>
    public override int GetHashCode()
    {
      return _guid.GetHashCode();
    }
 
    #endregion
 
    #region NewGuid
 
    /// <summary>
    /// Initialises a new instance of the ShortGuid class
    /// </summary>
    /// <returns></returns>
    public static ShortGuid NewGuid()
    {
      return new ShortGuid(Guid.NewGuid());
    }
 
    #endregion
 
    #region Encode
 
    /// <summary>
    /// Creates a new instance of a Guid using the string value,
    /// then returns the base64 encoded version of the Guid.
    /// </summary>
    /// <param name="value">An actual Guid string (i.e. not a ShortGuid)</param>
    /// <returns></returns>
    public static string Encode(string value)
    {
      Guid guid = new Guid(value);
      return Encode(guid);
    }
 
    /// <summary>
    /// Encodes the given Guid as a base64 string that is 22
    /// characters long.
    /// </summary>
    /// <param name="guid">The Guid to encode</param>
    /// <returns></returns>
    public static string Encode(Guid guid)
    {
      string encoded = Convert.ToBase64String(guid.ToByteArray());
      encoded = encoded
        .Replace("/", "_")
        .Replace("+", "-");
      return encoded.Substring(0, 22);
    }
 
    #endregion
 
    #region Decode
 
    /// <summary>
    /// Decodes the given base64 string
    /// </summary>
    /// <param name="value">The base64 encoded string of a Guid</param>
    /// <returns>A new Guid</returns>
    public static Guid Decode(string value)
    {
      value = value
        .Replace("_", "/")
        .Replace("-", "+");
      byte[] buffer = Convert.FromBase64String(value + "==");
      return new Guid(buffer);
    }
 
    #endregion
 
    #region Operators
 
    /// <summary>
    /// Determines if both ShortGuids have the same underlying
    /// Guid value.
    /// </summary>
    /// <param name="x"></param>
    /// <param name="y"></param>
    /// <returns></returns>
    public static bool operator ==(ShortGuid x, ShortGuid y)
    {
      if ((object)x == null) return (object)y == null;
      return x._guid == y._guid;
    }
 
    /// <summary>
    /// Determines if both ShortGuids do not have the
    /// same underlying Guid value.
    /// </summary>
    /// <param name="x"></param>
    /// <param name="y"></param>
    /// <returns></returns>
    public static bool operator !=(ShortGuid x, ShortGuid y)
    {
      return !(x == y);
    }
 
    /// <summary>
    /// Implicitly converts the ShortGuid to it's string equivilent
    /// </summary>
    /// <param name="shortGuid"></param>
    /// <returns></returns>
    public static implicit operator string(ShortGuid shortGuid)
    {
      return shortGuid._value;
    }
 
    /// <summary>
    /// Implicitly converts the ShortGuid to it's Guid equivilent
    /// </summary>
    /// <param name="shortGuid"></param>
    /// <returns></returns>
    public static implicit operator Guid(ShortGuid shortGuid)
    {
      return shortGuid._guid;
    }
 
    /// <summary>
    /// Implicitly converts the string to a ShortGuid
    /// </summary>
    /// <param name="shortGuid"></param>
    /// <returns></returns>
    public static implicit operator ShortGuid(string shortGuid)
    {
      return new ShortGuid(shortGuid);
    }
 
    /// <summary>
    /// Implicitly converts the Guid to a ShortGuid
    /// </summary>
    /// <param name="guid"></param>
    /// <returns></returns>
    public static implicit operator ShortGuid(Guid guid)
    {
      return new ShortGuid(guid);
    }
 
    #endregion
  }
}